package com.sanri.tools.modules.mock.l7.http.service;

import com.sanri.tools.modules.core.aspect.SerializerToFile;
import com.sanri.tools.modules.core.exception.ToolException;
import com.sanri.tools.modules.core.service.cc.CommonConfigService;
import com.sanri.tools.modules.core.service.file.FileManager;
import com.sanri.tools.modules.mock.l7.http.service.dtos.ResponseConfig;
import com.sanri.tools.modules.mock.l7.http.service.dtos.UrlBindAndResponseConfig;
import com.sanri.tools.modules.mock.l7.http.service.dtos.UrlBindResponseDto;
import com.sanri.tools.modules.mock.l7.http.service.strategy.RoundRobin;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Service
@Slf4j
public class ResponseHandlerManagerService implements InitializingBean {
    @Autowired
    private CommonConfigService commonConfigService;
    @Autowired
    private FileManager fileManager;

    private YamlPropertySourceLoader yamlPropertySourceLoader = new YamlPropertySourceLoader();

    /**
     * url => UrlBindResponseDto
     */
    private static final Map<String, UrlBindResponseDto> resposeConfigMatchUrlsMap = new ConcurrentHashMap<>();

    /**
     * url => RoundRobin(轮询实体)
     */
    private static final Map<String, RoundRobin> ROUND_ROBIN_MAP = new HashMap<>();

    /**
     * 列出所有模拟请求
     * @return
     */
    public Collection<UrlBindResponseDto> list(){
        return resposeConfigMatchUrlsMap.values();
    }

    /**
     * 用文本文件的方式编辑
     * @return
     */
    public String readInText() throws IOException {
        return fileManager.readConfig("vmock", "response/matchUrl");
    }

    public void writerInText(String content) throws IOException {
        fileManager.writeConfig("vmock", "response/matchUrl",content.toString());
    }

    /**
     * 编辑一个绑定
     * 方法执行后, 会序列化进文件
     * @param urlBindResponseDto
     */
    @SerializerToFile
    public void editUrlResponseBind(UrlBindResponseDto urlBindResponseDto){
        resposeConfigMatchUrlsMap.put(urlBindResponseDto.getUrl(),urlBindResponseDto);
    }

    @SerializerToFile
    public void deleteUrlConfig(String url){
        resposeConfigMatchUrlsMap.remove(url);
    }

    public void serializer() throws IOException {
        final Collection<UrlBindResponseDto> values = resposeConfigMatchUrlsMap.values();
        StringBuffer content = new StringBuffer();
        for (UrlBindResponseDto value : values) {
            content.append(value.getUrl()).append("|")
                    .append(value.getGroup()).append("|")
                    .append(value.getDescribe()).append("|")
                    .append(StringUtils.join(value.getResponseConfigNames(),",")).append("|")
                    .append(value.getStrategy()).append("\n");
        }
        fileManager.writeConfig("vmock", "response/matchUrl",content.toString());
    }

    PathMatcher pathMatcher = new AntPathMatcher();

    /**
     * 选择一个响应体返回
     * @param url
     * @return
     */
    public UrlBindAndResponseConfig choseResponse(String url) throws IOException {
        // 如果能直接匹配到 url 最好, 匹配不到的话就需要遍历所有配置项来找
        UrlBindResponseDto urlBindResponseDto = resposeConfigMatchUrlsMap.get(url);
        if (urlBindResponseDto == null){
            for (UrlBindResponseDto value : resposeConfigMatchUrlsMap.values()) {
                final String pattern = value.getUrl();
                final boolean match = pathMatcher.match(pattern, url);
                if (match){
                    urlBindResponseDto = value;
                    break;
                }
            }
        }

        if (urlBindResponseDto == null){
            throw new ToolException("未找到响应 url["+url+"] 配置");
        }

        final List<String> responseConfigNames = urlBindResponseDto.getResponseConfigNames();
        if (CollectionUtils.isEmpty(responseConfigNames)){
            throw new ToolException("没有配置当前 url["+url+"] 的响应映射");
        }
        final String strategy = urlBindResponseDto.getStrategy();
        String responeConfigName = "";
        switch (strategy){
            case "first":
                responeConfigName = responseConfigNames.get(0);
                break;
            case "last":
                responeConfigName = responseConfigNames.get(responseConfigNames.size() - 1);
                break;
            case "random":
                final int index = RandomUtils.nextInt(0, responseConfigNames.size());
                responeConfigName = responseConfigNames.get(index);
                break;
            case "roundRobin":
                final RoundRobin roundRobin = ROUND_ROBIN_MAP.computeIfAbsent(url, k -> new RoundRobin(responseConfigNames));
                responeConfigName = roundRobin.choseOne();
                break;
            default:
                throw new ToolException("不支持的策略:"+strategy);
        }

        final String content = commonConfigService.readTemplateFileContent("response.yaml", responeConfigName);
        final ByteArrayResource byteArrayResource = new ByteArrayResource(content.getBytes(StandardCharsets.UTF_8));
        final List<PropertySource<?>> load = yamlPropertySourceLoader.load("a", byteArrayResource);
        Iterable<ConfigurationPropertySource> from = ConfigurationPropertySources.from(load);
        Binder binder = new Binder(from);
        BindResult<ResponseConfig> bind = binder.bind("", ResponseConfig.class);
        final ResponseConfig responseConfig = bind.get();

        return new UrlBindAndResponseConfig(urlBindResponseDto,responseConfig);
    }

    /**
     * url   |响应配置1, 响应配置2 | 策略(first, last, random, roundRobin)
     * url1  | 响应配置名1, 响应配置2| first
     * url2  | 响应配置名1, 响应配置2 | random
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        final String vmock = fileManager.readConfig("vmock", "response/matchUrl");
        if (StringUtils.isNotBlank(vmock)){
            final String[] lines = StringUtils.split(vmock, "\n");
            for (String line : lines) {
                if (line.startsWith("#")){
                    // 注释内容不做处理
                    continue;
                }
                final String[] items = StringUtils.splitPreserveAllTokens(line, "|");
                String url = StringUtils.trim(items[0]);
                String group = StringUtils.trim(items[1]);
                String describe = StringUtils.trim(items[2]);
                String [] responseConfigNames = StringUtils.split(items[3],",");
                Arrays.stream(responseConfigNames).forEach(StringUtils::trim);
                String strategy = StringUtils.trim(items[4]);
                resposeConfigMatchUrlsMap.put(url, new UrlBindResponseDto(url,group, describe, Arrays.asList(responseConfigNames), strategy));
            }
        }
    }


}
